/*
 * MainFrame.java
 *
 * Created on September 4, 2008, 2:14 PM
 */
package com.dksoft.view;

import java.awt.Component;
import java.awt.Container;
import java.awt.event.KeyEvent;
import java.text.ParseException;
import pojo.HourTaskForm;
import com.dksoft.controller.ReminderController;
import com.dksoft.model.CalendarMethod;
import com.dksoft.model.RecursiveReference;
import java.awt.AWTException;
import java.awt.FocusTraversalPolicy;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.Point;
import java.awt.PopupMenu;
import java.awt.Robot;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JViewport;
import javax.swing.Timer;
import org.apache.log4j.Logger;
import com.dksoft.model.Reminder;
import com.dksoft.model.ReminderManager;
import java.util.Date;
import java.util.ResourceBundle;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu.Separator;
import pojo.ReminderTaskForm;
/**
 * @author  Dickson
 * @version 1.0
 * @since 2008 June
 */
public class MainFrame extends JFrame implements PropertyChangeListener, MouseListener, ActionListener, 
                                                     WindowListener, AdjustmentListener, KeyListener {

    private static Logger logger = Logger.getLogger(MainFrame.class.getName());
    private static Calendar currentCalendar = Calendar.getInstance();
    private static int taskPanelWidth;
    private int oldHourIndex = 0; // just a variable as HourSelector firePropertyChange old value = -1
    private int currentHourIndex = 0;
    private static String year;// = String.valueOf(currentCalendar.get(Calendar.YEAR));
    private static String month;// = currentCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault());
    private static String day;// = (currentCalendar.get(Calendar.DAY_OF_MONTH) < 10)
                                //? "0" + String.valueOf(currentCalendar.get(Calendar.DAY_OF_MONTH))
                                //: String.valueOf(currentCalendar.get(Calendar.DAY_OF_MONTH));
    private SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
    private SimpleDateFormat reminderTimeFormat = new SimpleDateFormat("HH:mm"); // not using titleFormat because reminder check every minute, not second
    private SimpleDateFormat calendarDateFormat = new SimpleDateFormat("dd-MMM-yyyy");
    private JViewport taskViewPort = new JViewport();
    private JViewport calendarViewPort = new JViewport();
    private ReminderTask[] pnReminderTask = new ReminderTask[24];
    private MonthCalendar[] pnMonthCalendar = new MonthCalendar[12];
    private static List<Reminder> dayReminderList = null;
    private static List<Reminder> todayReminderList = new ArrayList();
    private ClassLoader cl = this.getClass().getClassLoader();
    private TrayIcon trayIcon;
    private SystemTray systemTray;
    private ResourceBundle resourceBundle;
    private YearSelector cmbYearSelector = new YearSelector();
    private HourSelector cmbHourSelector = new HourSelector();
    private ReminderController reminderController = new ReminderController();
    private ReminderManager reminderManager = new ReminderManager();
    private static int reminderTaskTxtRow = 0;
    private static boolean hourChangeUsingKeyPress = false;
    private static JLabel currentDayLabel = null; // store calendar JLabel when day label clicked

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        pnCalendarWest = new javax.swing.JPanel();
        pnYear = new GradientPanel("panel.top.header.color.start", "panel.top.header.color.end", "V");
        lblYear = new javax.swing.JLabel();
        spCalendar = new javax.swing.JScrollPane();
        pnCalendar = new javax.swing.JPanel();
        pnReminderCenter = new javax.swing.JPanel();
        pnDateTimeNorth = new GradientPanel("panel.top.header.color.start", "panel.top.header.color.end", "V");
        lblDate = new javax.swing.JLabel();
        hourComboPanel = new GradientPanel("panel.top.header.color.start", "panel.top.header.color.end", "V");
        lblHour = new javax.swing.JLabel();
        lblTime = new javax.swing.JLabel();
        pnTaskCenter = new javax.swing.JPanel();
        tpTaskCenter = new javax.swing.JTabbedPane();
        spReminderCenter = new javax.swing.JScrollPane();
        pnReminder = new javax.swing.JPanel();
        menuBar = new javax.swing.JMenuBar();
        mnuTools = new javax.swing.JMenu();
        mnuItemInputText = new javax.swing.JMenuItem();
        mnuItemGotoDate = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setTitle(resourceBundle.getBundle("label").getString("mainframe.title")); // NOI18N
        setMinimumSize(new java.awt.Dimension(800, 600));

        pnCalendarWest.setBackground(new java.awt.Color(204, 255, 255));
        pnCalendarWest.setPreferredSize(new java.awt.Dimension(220, 250));
        pnCalendarWest.setLayout(new java.awt.BorderLayout());

        pnYear.setPreferredSize(new java.awt.Dimension(80, 30));

        lblYear.setFont(new java.awt.Font("Tahoma", 0, 13));
        lblYear.setForeground(new java.awt.Color(255, 255, 255));
        lblYear.setText("Year :");
        lblYear.setName("lblYear"); // NOI18N
        pnYear.add(lblYear);

        pnCalendarWest.add(pnYear, java.awt.BorderLayout.NORTH);

        spCalendar.setMaximumSize(new java.awt.Dimension(10, 10));
        spCalendar.setMinimumSize(new java.awt.Dimension(10, 10));
        spCalendar.setPreferredSize(new java.awt.Dimension(10, 10));

        pnCalendar.setPreferredSize(new java.awt.Dimension(200, 1800));
        pnCalendar.setLayout(new javax.swing.BoxLayout(pnCalendar, javax.swing.BoxLayout.Y_AXIS));
        spCalendar.setViewportView(pnCalendar);

        pnCalendarWest.add(spCalendar, java.awt.BorderLayout.CENTER);

        getContentPane().add(pnCalendarWest, java.awt.BorderLayout.WEST);

        pnReminderCenter.setLayout(new java.awt.BorderLayout());

        pnDateTimeNorth.setPreferredSize(new java.awt.Dimension(20, 30));
        pnDateTimeNorth.setLayout(new java.awt.GridLayout(1, 3));

        lblDate.setFont(new java.awt.Font("Tahoma", 0, 18));
        lblDate.setForeground(new java.awt.Color(255, 255, 255));
        lblDate.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        lblDate.setText("Date");
        lblDate.setPreferredSize(new java.awt.Dimension(100, 21));
        pnDateTimeNorth.add(lblDate);

        lblHour.setFont(new java.awt.Font("Tahoma", 0, 13));
        lblHour.setForeground(new java.awt.Color(255, 255, 255));
        lblHour.setText("Hour : ");
        hourComboPanel.add(lblHour);

        pnDateTimeNorth.add(hourComboPanel);

        lblTime.setBackground(new java.awt.Color(204, 204, 204));
        lblTime.setFont(new java.awt.Font("Tahoma", 0, 18));
        lblTime.setForeground(new java.awt.Color(255, 255, 255));
        lblTime.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        lblTime.setText("Timer");
        lblTime.setPreferredSize(new java.awt.Dimension(100, 21));
        pnDateTimeNorth.add(lblTime);

        pnReminderCenter.add(pnDateTimeNorth, java.awt.BorderLayout.NORTH);

        pnTaskCenter.setMaximumSize(new java.awt.Dimension(100, 100));
        pnTaskCenter.setMinimumSize(new java.awt.Dimension(100, 100));
        pnTaskCenter.setLayout(new java.awt.BorderLayout());

        tpTaskCenter.setMinimumSize(new java.awt.Dimension(800, 500));
        tpTaskCenter.setOpaque(true);
        tpTaskCenter.setPreferredSize(new java.awt.Dimension(800, 700));

        pnReminder.setAutoscrolls(true);
        pnReminder.setFocusTraversalPolicyProvider(true);
        pnReminder.setLayout(new javax.swing.BoxLayout(pnReminder, javax.swing.BoxLayout.Y_AXIS));
        spReminderCenter.setViewportView(pnReminder);

        tpTaskCenter.addTab("Reminder", spReminderCenter);

        pnTaskCenter.add(tpTaskCenter, java.awt.BorderLayout.CENTER);
        tpTaskCenter.getAccessibleContext().setAccessibleName("Reminder");

        pnReminderCenter.add(pnTaskCenter, java.awt.BorderLayout.CENTER);

        getContentPane().add(pnReminderCenter, java.awt.BorderLayout.CENTER);

        mnuTools.setMnemonic('T');
        mnuTools.setText("Tools");

        mnuItemInputText.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_I, java.awt.event.InputEvent.ALT_MASK));
        mnuItemInputText.setText("Input Text");
        mnuItemInputText.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mnuItemInputTextActionPerformed(evt);
            }
        });
        mnuTools.add(mnuItemInputText);

        mnuItemGotoDate.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_G, java.awt.event.InputEvent.ALT_MASK));
        mnuItemGotoDate.setText("Goto Date");
        mnuItemGotoDate.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mnuItemGotoDateActionPerformed(evt);
            }
        });
        mnuTools.add(mnuItemGotoDate);

        menuBar.add(mnuTools);

        setJMenuBar(menuBar);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void mnuItemInputTextActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mnuItemInputTextActionPerformed
        pnReminderTask[currentHourIndex].enableFocusField(reminderTaskTxtRow);
        ReminderTask.tmpText = pnReminderTask[currentHourIndex].txtTask[reminderTaskTxtRow].getText();
        //System.out.println("===" + ReminderTask.tmpText);
}//GEN-LAST:event_mnuItemInputTextActionPerformed

    private void mnuItemGotoDateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mnuItemGotoDateActionPerformed
        DateDialog dd = new DateDialog(this, true);
        dd.addPropertyChangeListener(this);
        dd.setLocationRelativeTo(null);
        dd.setVisible(true);
    }//GEN-LAST:event_mnuItemGotoDateActionPerformed

    public MainFrame() {
        logger.info("Initialize Mainframe...");
        initDatabase();
        initComponents();
        initMainFrame();
        updateCalendarUI(currentCalendar.get(Calendar.YEAR));
        updateReminderTaskUI();
        initTodayReminder();
        pack();
        logger.info("Mainframe initialized successfully");
    }

    private void initMainFrame() {
        // Show Year Selector ComboBox
        cmbYearSelector.addPropertyChangeListener(this);
        cmbYearSelector.addKeyListener(this);
        pnYear.add(cmbYearSelector);

        // Show Hour Selector ComboBox
        cmbHourSelector.addPropertyChangeListener(this);
        cmbHourSelector.addKeyListener(this);
        hourComboPanel.add(cmbHourSelector);

        // Add tpTaskCenter listerner for goto date dialog box, since there will be only 3 item on focus,
        // which is cmbYear, cmbHour tab panel
        tpTaskCenter.addKeyListener(this);
        
        // Show initial date
        lblDate.setText(calendarDateFormat.format(currentCalendar.getTime()).toString());

        // Show Timer
        Timer secondTimer = new Timer(1000, this);
        secondTimer.setActionCommand("secondTimer");
        secondTimer.start();

        // add reminder checker timer to check reminder every 60 second = 1 min
        Timer reminderTimer = new Timer(60000, this);
        reminderTimer.setActionCommand("reminderTimer");
        reminderTimer.start();

        // Add adjustmentListerner to spreminderCenter
        spReminderCenter.getVerticalScrollBar().addAdjustmentListener(this);
        
        // Initialize system tray property
        if (SystemTray.isSupported()) {
            PopupMenu popup = new PopupMenu();
            MenuItem menuItem = new MenuItem("Exit Reminder");
            menuItem.setActionCommand("trayExit");
            menuItem.addActionListener(this);
            popup.add(menuItem);

            systemTray = SystemTray.getSystemTray();
            Image image = Toolkit.getDefaultToolkit().getImage(cl.getResource("image/clock.jpg"));
            trayIcon = new TrayIcon(image, "Reminder", popup);
            trayIcon.setImageAutoSize(true);
            trayIcon.addMouseListener(this);
            try {
                systemTray.add(trayIcon);
            } catch (AWTException ex) {
                logger.debug("System tray added fail: " + ex.toString());
            }
        } else {
            logger.debug("System tray is currently not supported.");
        }
        
        // add Listener to MainFrame
        this.addWindowListener(this);
        this.setFocusTraversalPolicy(focusTraversalPolicy);
        
        // add image icon for mainframe
        this.setIconImage(new ImageIcon(cl.getResource("image/clock.jpg")).getImage());

        // scroll spCalendar to corresponding month
        gotoMonth(currentCalendar.get(Calendar.MONTH));
    }

    private void initDatabase() {
        //Copy database if not exist
        String dbPath = CalendarMethod.getDBPath();
        if (CalendarMethod.isFileExist(dbPath) == false) {
            CalendarMethod.createDatabase(dbPath);
        }

        // Run init database script if exist
//        if (CalendarMethod.runDatabaseScript() == false) {
//            JOptionPane.showMessageDialog(null, "Fail to initialize database, system exiting now.", "Error", JOptionPane.ERROR_MESSAGE);
//            System.exit(0);
//        }
    }
    /**
     * Use Robot class to move the mouse cursor to specific coordinate
     * @param x
     * @param y
     */
    private void moveMouse(int x, int y){
        try {
            Robot robot = new Robot();
            robot.mouseMove(x, y);
        } catch (AWTException ex) {
            logger.debug("Robot.moveMouse is currently not supported: " + ex.toString());
        }
        logger.info("[moveMouse]: x=" + x +", y=" +y);
    }
    
    /**
     * Initialize ALL calendar in pnCalendar or update when year change
     * @param int currentYear
     */
    private void updateCalendarUI(int currentYear) {
        pnCalendar.removeAll();
        for (int i = 0; i < 12; i++) {
            pnMonthCalendar[i] = new MonthCalendar(currentYear, i, -1);
            pnMonthCalendar[i].addPropertyChangeListener(this);
            pnCalendar.add(pnMonthCalendar[i]);
        }
        logger.info("[updateCalendarUI]: currentYear= " + currentYear);
    }

    /**
     * Initialize ALL reminder task 
     */
    private void updateReminderTaskUI() {
        //first hour in a day is 12 a.m, so is not in the below loop list
        pnReminderTask[0] = new ReminderTask(24);
        pnReminderTask[0].addPropertyChangeListener(this);
        pnReminder.add(pnReminderTask[0]);
        for (int i = 1; i < 24; i++) {
            pnReminderTask[i] = new ReminderTask(i);
            Separator separator = new Separator();
            //separator.setBackground(CalendarMethod.getColor("datetime.panel.color"));
            pnReminderTask[i].addPropertyChangeListener(this);            
            pnReminder.add(separator);
            pnReminder.add(pnReminderTask[i]);
        }

        //initialize taskPanelWidth to use when adjustmentValueChanged for scroll bar
        taskPanelWidth = pnReminder.getPreferredSize().height / 24;
        
        logger.info("[updateReminderTaskUI]");
        //Advertisement ad = new Advertisement();
        //pnReminder.add(new Advertisement());
    }

   /**
    * Update HourSelector color, if highlight = true, then colour the text, else, reset the text colour
    * @param hourValue
    * @param highlight
    */
    private void updateHourCmbBoxColor(int hourValue, boolean highlight) {
        List<HourTaskForm> list = cmbHourSelector.hourFormList;
        for (int i = 0; i < list.size(); i++) {
            HourTaskForm form = (HourTaskForm) list.get(i);
            if (form.getHourValue() == hourValue) {
                if (highlight) form.setChecked(true);
                else form.setChecked(false);
                break;
            }
        }
        cmbHourSelector.resetColor();
        logger.info("[updateHourCmbBoxColor]: hourValue= " + hourValue);
    }

    /**
     * Reset HourSelector color to default 
     */
     private void resetHourCmbBoxColor() {
        List<HourTaskForm> list = cmbHourSelector.hourFormList;
        for (int i = 0; i < list.size(); i++) {
            HourTaskForm form = (HourTaskForm) list.get(i);
            form.setChecked(false);
        }
        cmbHourSelector.resetColor();
        logger.info("[resetHourCmbBoxColor]");
    }
     
     /**
      * Update panel color, set viewport and highlight reminder task text field when hour change
      * @param oldIndex previous hour index value
      * @param newIndex current hour index value
      * @param reminderRow row to highlight
      * @param fromMouseClick if action is call from mouse click, text field did not highlight since mouse cursor moving
      */
    private void performHourChange(int oldIndex, int newIndex, int reminderRow, boolean fromMouseClick) {
        updateHourPanelColor(oldIndex, newIndex); //update hour and time format colour
        gotoHour(newIndex);
        cmbHourSelector.setSelectedIndex(newIndex);
        pnReminderTask[oldIndex].unHighlightFocusField(reminderTaskTxtRow);
        pnReminderTask[newIndex].txtTask[reminderRow].requestFocusInWindow();
        reminderTaskTxtRow = reminderRow;
        if (fromMouseClick == false) {
            moveMouse(cmbHourSelector.getLocationOnScreen().x + 88, cmbHourSelector.getLocationOnScreen().y + 6);
            hourChangeUsingKeyPress = true;
            pnReminderTask[newIndex].highlightFocusField(reminderRow);
        }
        logger.info("[performHourChange]: oldIndex=" + oldIndex + ", newIndex=" + newIndex + ", reminderRow=" + reminderRow + ", fromMouseClick=" + fromMouseClick);
        
    }
     
    /**
     * Update UI when year change
     * @param year
     */
    private void performYearChange(int year) {
        int currentYear = year;
        updateCalendarUI(currentYear);
        clearReminderTask();
        if(year == CalendarMethod.getThisYear())
        displayDayTask(todayReminderList);
        logger.info("[performYearChange]: year=" + year);
    }
    
    /**
     * Update reminder task field and set panel date
     * @param date in dd-MMM-yyyy format
     */
    private void performDayChange(JLabel label){
        currentDayLabel = label;
        lblDate.setText(label.getName());
        resetStaticVariable(label.getName());
        Date date = reminderController.constructDate(lblDate);
        dayReminderList = reminderManager.getReminderByDate(date);
        displayDayTask(dayReminderList);
        logger.info("[performDayChange]: date=" + label.getName());
    }

    private void performDayChangeFromDialog(String dateToGo) {
        try {
            resetStaticVariable(dateToGo);
            lblDate.setText(dateToGo);
            Date date = calendarDateFormat.parse(dateToGo);
            dayReminderList = reminderManager.getReminderByDate(date);
            displayDayTask(dayReminderList);
            int iYear = Integer.parseInt(MainFrame.year);
            int iMonth = CalendarMethod.getMonth(MainFrame.month);
            int iDay = Integer.parseInt(MainFrame.day);
            cmbYearSelector.setSelectedIndex(iYear - 2009); // this will directly call performYearChange() since cmbYearSelector implement itemChange Listerner
            pnMonthCalendar[iMonth].setGotoDate(pnMonthCalendar[iMonth].lblDayArr[iDay]);
            gotoMonth(iMonth);
            logger.info("[performDayChange]: date=" + dateToGo);
        } catch (ParseException ex) {
            logger.info("[performDayChangeFromDialog Fail]: " + ex.toString());
        }
    }

    /**
     * Add new reminder entry into database
     * @param hourTaskForm
     */
    private void performAddReminder(ReminderTaskForm reminderTaskForm) {
        if (!reminderTaskForm.getReminderTask().trim().equals("")) {
            int hour = reminderTaskForm.getHour() == 24 ? 0 : reminderTaskForm.getHour();
            updateHourCmbBoxColor(hour, true); //locally update color for day choosed, not get from DB
            if(currentDayLabel != null) pnMonthCalendar[0].setBorderedTaskDay(currentDayLabel); // any pnMonthCalendar can call here, since just need it to execute the setBorderedTaskDay method
            try {
                Reminder reminder = reminderController.constructReminder(reminderTaskForm, lblDate);
                logger.info("[performAddReminder]: " + reminder.toString());
                reminderController.updateReminder(reminder); // add/update into database
                //montlyReminderList.add(reminder); // add/update into local reminder list
                // if add reminder date = today date, then update todayReminderList
                if (calendarDateFormat.format(reminder.getDate()).equals(calendarDateFormat.format(Calendar.getInstance().getTime()))) {
                    todayReminderList.add(reminder);
                }                
            } catch (Exception ex) {
                logger.debug("performAddReminder Fail: " + ex.toString());
            }
        }
    }
    
    /**
     * Un highlight field when hour change from cmbHourSelector initially, then mouse using for navigate
     */
    private void performUnHighlightField() {
        if (hourChangeUsingKeyPress == true) {
            pnReminderTask[currentHourIndex].unHighlightFocusField(reminderTaskTxtRow);
            hourChangeUsingKeyPress = false;
        }
    }

    /**
     * Highlight reminder task text field when key press
     * @param value Determine whether up or down arrow key press
     */
    private void performFocusReminderField(String value) {
        
        pnReminderTask[currentHourIndex].unHighlightFocusField(reminderTaskTxtRow);
        if (value.equals("keyUp")) {
            if (reminderTaskTxtRow > 0) {
                reminderTaskTxtRow -= 1;
                pnReminderTask[currentHourIndex].highlightFocusField(reminderTaskTxtRow);
            } else if (reminderTaskTxtRow == 0 && currentHourIndex > 0) {
                oldHourIndex = currentHourIndex;
                currentHourIndex -= 1;
                reminderTaskTxtRow = 11;
                performHourChange(oldHourIndex, currentHourIndex, reminderTaskTxtRow, false);
            } else if (reminderTaskTxtRow == 0 && currentHourIndex == 0) {
                pnReminderTask[currentHourIndex].highlightFocusField(reminderTaskTxtRow);
            }

        } else if (value.equals("keyDown")) {
            if (reminderTaskTxtRow < 11) {
                reminderTaskTxtRow += 1;
                pnReminderTask[currentHourIndex].highlightFocusField(reminderTaskTxtRow);
            } else if (reminderTaskTxtRow == 11 && currentHourIndex < 23) {
                oldHourIndex = currentHourIndex;
                currentHourIndex += 1;
                reminderTaskTxtRow = 0;
                performHourChange(oldHourIndex, currentHourIndex, reminderTaskTxtRow, false);
            } else if (reminderTaskTxtRow == 11 && currentHourIndex == 23) {
                pnReminderTask[currentHourIndex].highlightFocusField(reminderTaskTxtRow);
            }
        }
    }

//    /**
//     * Enable reminder task text field for input
//     */
//    private void performAcceptInput(){
//        pnReminderTask[currentHourIndex].enableFocusField(reminderTaskTxtRow);
//    }


    private void performShowRecursiveDialog(ReminderTaskForm reminderTaskForm){
        try {
            Reminder reminder = reminderController.constructReminder(reminderTaskForm, lblDate);
            reminder = reminderController.getReminder(reminder);
            RecursiveDialog rd = new RecursiveDialog(this, true, reminder);
            rd.addPropertyChangeListener(this);
            rd.setLocationRelativeTo(null);
            rd.setVisible(true);
        } catch (Exception ex) {
            logger.debug("performShowRecursiveDialog Fail: " + ex.toString());
        }

    }

    /**
     * add 'R' label in current textfield to indicate it's Recursive task
     * @param reminder
     */
    private void performAddRecursive(Reminder reminder){
        SimpleDateFormat shf = new SimpleDateFormat("HH");
        SimpleDateFormat smf = new SimpleDateFormat("mm");
        String hour = shf.format(reminder.getTime());
        String minute = smf.format(reminder.getTime());
        pnReminderTask[Integer.parseInt(hour)].addRecursiveLabel(getTaskRow(minute));
    }

    /**
     * Remove 'R' label in current textfield
     */
    private void performDeleteRecursive(Reminder reminder){
        deleteRecursiveLabel(reminder);
    }

    private void performDeleteReminder(ReminderTaskForm reminderTaskForm) {
        try {
            int hour = reminderTaskForm.getHour() == 24 ? 0 : reminderTaskForm.getHour();
            Reminder reminder = reminderController.constructReminder(reminderTaskForm, lblDate);
            reminderController.deleteReminder(reminder); // delete reminder from database
            // if add reminder date = today date, then update todayReminderList
            if (calendarDateFormat.format(reminder.getDate()).equals(calendarDateFormat.format(Calendar.getInstance().getTime()))) {
                removeElementInList(todayReminderList, reminder);
            }
            // check if there is any task for specific day, if no, then restore label
            if (taskExistInDay(reminder) == false) {
                updateHourCmbBoxColor(hour, false); //locally update color for day choosed, not get from DB
                pnMonthCalendar[0].removeTaskBorder(currentDayLabel); // set label to normal style if there is no task
            //check if there is any task for specific hour, if no, then restore default colour to hour cmb box
            } else if (taskExistInHour(reminder, hour) == false) {
                updateHourCmbBoxColor(hour, false); //locally update color for day choosed, not get from DB
            }
            // remove 'R label for recursive task
            deleteRecursiveLabel(reminder);
            logger.info("[performDeleteReminder]: " + reminderTaskForm.toString());
        } catch (Exception ex) {
            logger.debug("performDeleteReminder Fail: " + ex.toString());
        }
    }

    /**
     * Check the existency of task in current day
     * @param reminder
     * @return Boolean
     * @throws java.text.ParseException
     */
    private boolean taskExistInDay(Reminder reminder) throws ParseException {
        List dayList = reminderManager.getReminderByDate(reminder.getDate());
        if(dayList != null) return true;
        return false;
    }

    /**
     * Check the existency of task in current hour
     * @param reminder
     * @param intHour
     * @return Boolean
     */
    private boolean taskExistInHour(Reminder reminder, int intHour) {
        List dayList = reminderManager.getReminderByDate(reminder.getDate());
        Iterator it = dayList.iterator();
        SimpleDateFormat shf = new SimpleDateFormat("HH");
        String hour = null;
        while (it.hasNext()) {
            Reminder dayReminder = (Reminder) it.next();
            hour = shf.format(dayReminder.getTime());
            if (Integer.parseInt(hour) == intHour) {
                return true;
            }
        }
        return false;
    }

    private void removeElementInList(List list, Reminder reminder) {
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Reminder rem = (Reminder) it.next();
            if (rem.getDate().equals(reminder.getDate()) && rem.getTime().equals(reminder.getTime()) ) {
                it.remove();
                break;
            }
        }
    }
    /**
     * Set viewport based on hour selected
     * @param currentHourIndex
     */
    private void gotoHour(int currentHourIndex) {
        taskViewPort.setView(pnReminder);
        Point pt = taskViewPort.getViewPosition();
        pt.x = 0;
        pt.y = currentHourIndex > 0 ? (((currentHourIndex) * taskPanelWidth)) - taskPanelWidth : 0;
        taskViewPort.setViewPosition(pt);
        spReminderCenter.setViewport(taskViewPort);
    }

    private void deleteRecursiveLabel(Reminder reminder){
        SimpleDateFormat shf = new SimpleDateFormat("HH");
        SimpleDateFormat smf = new SimpleDateFormat("mm");
        String hour = shf.format(reminder.getTime());
        String minute = smf.format(reminder.getTime());
        pnReminderTask[Integer.parseInt(hour)].removeRecursiveLabel(getTaskRow(minute));
    }
    /**
     * Set viewport based on month selected, viewport will set to 3 section according to month
     * @param month
     */
    private void gotoMonth(int month){
        int range = 0;
        calendarViewPort.setView(pnCalendar);
        Point pt = calendarViewPort.getViewPosition();
        pt.x = 0;
            if(month >= 4 && month < 8) range = 1;
            else if(month >= 8 && month <=12 ) range = 2;
        pt.y = pnCalendar.getHeight()/3 * range;
        calendarViewPort.setViewPosition(pt);
        spCalendar.setViewport(calendarViewPort);
    }
    
    /**
     * Update HourPanel color
     * @param oldHourIndex
     * @param newHourIndex
     */
    private void updateHourPanelColor(int oldHourIndex, int newHourIndex) {
        pnReminderTask[oldHourIndex].removeHourPanelFocus();
        pnReminderTask[newHourIndex].setHourPanelFocus();
    }

    /**
     * Update displayed day label value
     * @param date
     */
    private void resetStaticVariable(String date) {
        MainFrame.day = date.substring(0, 2);
        MainFrame.month = date.substring(3, 6);
        MainFrame.year = date.substring(7, 11);
    }

    /**
     * Update displayed day task
     * @param List<Reminder>
     * @param day
     */
    private void displayDayTask(List<Reminder> list) {
        clearReminderTask();
        resetHourCmbBoxColor();
        if (list != null) {
            Iterator it = list.iterator();
            SimpleDateFormat shf = new SimpleDateFormat("HH");
            SimpleDateFormat smf = new SimpleDateFormat("mm");
            String hour = null;
            String minute = null;

            while (it.hasNext()) {
                Reminder reminder = (Reminder) it.next();
                //if (day.equals(sdf.format(reminder.getDate()))) {
                hour = shf.format(reminder.getTime());
                minute = smf.format(reminder.getTime());
                pnReminderTask[Integer.parseInt(hour)].txtTask[getTaskRow(minute)].setText(reminder.getTask());
                //when hour=24am, database will store as 00, so pnReminderTask[0] can be use, not pnReminderTask[24]
                updateHourCmbBoxColor(Integer.parseInt(hour), true);

                if (reminder.getRecursiveId() != null) {
                    performAddRecursive(reminder);
                }
            //}
            }
            logger.info("[displayDayTask]: day=" + MainFrame.day);
        }
    }

    /**
     * Initialize montlyReminderList to current month for initialize todayReminderList
     */
    private void initTodayReminder() {
        Date date = reminderController.constructDate(lblDate);
        if (reminderManager.getReminderByDate(date) != null) {
            todayReminderList = reminderManager.getReminderByDate(date);
            displayDayTask(todayReminderList);
            logger.info("[initTodayReminder]");
        }
    }

    /**
     * Reset ReminderTask value and "R" label if exist
     */
    private void clearReminderTask() {
        for (int i = 0; i < 24; i++) {
            for (int row = 0; row < 12; row++) {
                pnReminderTask[i].txtTask[row].setText("");
                pnReminderTask[i].removeRecursiveLabel(row);
            }
        }
    }

    /**
     * Return row number based on String minutes in ReminderTask
     * @param minute
     * @return row number
     */
    private int getTaskRow(String minute) {
        int iMinute = Integer.parseInt(minute);
        int row = 0;
        switch (iMinute) {
            case 0: row = 0; break;
            case 5: row = 1; break;
            case 10:row = 2; break;
            case 15:row = 3; break;
            case 20:row = 4; break;
            case 25:row = 5; break;
            case 30:row = 6; break;
            case 35:row = 7; break;
            case 40:row = 8; break;
            case 45:row = 9; break;
            case 50:row = 10;break;
            case 55:row = 11;break;
        }
        return row;
    }

    /**
     * Check reminder list whether it should prompt up reminder dialog
     * @param reminder
     * @return boolean
     */
    private boolean isAlert(Reminder reminder) {
        String timeNow = reminderTimeFormat.format(Calendar.getInstance().getTime());
        String reminderTime = reminderTimeFormat.format(reminder.getTime());
        System.out.println("time now = " + timeNow);
        System.out.println("reminderTime = " + reminderTime);
        System.out.println("reminder = " + reminder.toString());
        if (reminderTime.equals(timeNow))  return true;

        else if(reminder.getRecursiveId() != null){
           if(isRecursiveSchedule(reminder.getRecursiveId()) && reminderTime.equals(timeNow)) return true;
        }

        if (reminder.getSnoozeTime() != null) {
            String snoozeTime = reminderTimeFormat.format(reminder.getSnoozeTime());
            if (snoozeTime.equals(timeNow)) return true;
        }

        return false;
    }

    /**
     * Check whether it's recursive task
     * @param id
     * @return Boolean
     */
    private boolean isRecursiveSchedule(long id){
        String dayOfWeek = currentCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault());
        int dayOfMonth = currentCalendar.get(Calendar.DAY_OF_MONTH);
        RecursiveReference ref = reminderManager.getRecursive(id);
            if(dayOfWeek.equals("Monday") && ref.getMonday() == 1) return true;
            else if(dayOfWeek.equals("Tuesday") && ref.getTuesday() == 1) return true;
            else if(dayOfWeek.equals("Wednesday") && ref.getWednesday() == 1) return true;
            else if(dayOfWeek.equals("Thursday") && ref.getThursday() == 1) return true;
            else if(dayOfWeek.equals("Friday") && ref.getFriday() == 1) return true;
            else if(dayOfWeek.equals("Saturday") && ref.getSaturday() == 1) return true;
            else if(dayOfWeek.equals("Sunday") && ref.getSunday() == 1) return true;
            else if(dayOfMonth == ref.getMonthly()) return true;
         return false;
    }
    // <editor-fold defaultstate="collapsed" desc="PropertyChangeListener">
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals("yearChange")) {
            performYearChange((Integer) evt.getNewValue());

        } else if (evt.getPropertyName().equals("dayChange")) {
            performDayChange((JLabel) evt.getNewValue());

        } else if (evt.getPropertyName().equals("dayChangeFromDateDialog")) {
            performDayChangeFromDialog((String) evt.getNewValue());

        } else if (evt.getPropertyName().equals("mouseClickHourChange")) {
            oldHourIndex = currentHourIndex;
            currentHourIndex = (Integer) evt.getNewValue();
            performHourChange(oldHourIndex, currentHourIndex, 0, true);
          
        } else if (evt.getPropertyName().equals("keyPressHourChange")) {
            oldHourIndex = currentHourIndex;
            currentHourIndex = (Integer) evt.getNewValue();
            performHourChange(oldHourIndex, currentHourIndex, 0, false);
          
        } else if (evt.getPropertyName().equals("reminderTaskKeyUp")) {
            performFocusReminderField("keyUp");

        } else if (evt.getPropertyName().equals("reminderTaskKeyDown")) {
            performFocusReminderField("keyDown"); 

        } else if (evt.getPropertyName().equals("reminderTaskMouseEnter")) {
            performUnHighlightField();
            
//        } else if (evt.getPropertyName().equals("acceptInput")) {
//            performAcceptInput();

        } else if (evt.getPropertyName().equals("addReminder")) {
            performAddReminder((ReminderTaskForm) evt.getNewValue());

        } else if (evt.getPropertyName().equals("showRecursiveDialog")) {
            performShowRecursiveDialog((ReminderTaskForm) evt.getNewValue());

        } else if (evt.getPropertyName().equals("deleteReminder")){
            performDeleteReminder((ReminderTaskForm) evt.getNewValue());
        
        } else if (evt.getPropertyName().equals("addOrUpdateRecursive")){
            performAddRecursive((Reminder)evt.getNewValue());
        
        } else if (evt.getPropertyName().equals("deleteRecursive")){
            performDeleteRecursive((Reminder)evt.getNewValue());
        }


    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="FocusTraversalPolicy">
    FocusTraversalPolicy focusTraversalPolicy = new FocusTraversalPolicy() {

        @Override
        public Component getComponentAfter(Container aContainer, Component aComponent) {
            if (aComponent == cmbYearSelector){ return cmbHourSelector;}
            else if (aComponent == cmbHourSelector){ return tpTaskCenter; }
            else if (aComponent == tpTaskCenter){ return cmbYearSelector; }
            return cmbHourSelector;
        }

        @Override
        public Component getComponentBefore(Container aContainer, Component aComponent) {
            if (aComponent == tpTaskCenter){ return cmbHourSelector;}
            else if (aComponent == cmbHourSelector){ return cmbYearSelector; }
            else if (aComponent == cmbYearSelector){ return tpTaskCenter; }
            return cmbHourSelector;
        }

        @Override
        public Component getFirstComponent(Container aContainer) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public Component getLastComponent(Container aContainer) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public Component getDefaultComponent(Container aContainer) {
            return cmbHourSelector;
        }
    };
       
     // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="AdjustmentListener">
    @Override
    public void adjustmentValueChanged(AdjustmentEvent e) {
        e.getAdjustable().setUnitIncrement(taskPanelWidth / 3);
    }
// </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="ActionListener">
    @Override
    public void actionPerformed(ActionEvent e) {
        String actionCommand = e.getActionCommand();
        if (actionCommand.equals("secondTimer")) {
            String time = timeFormat.format(Calendar.getInstance().getTime());
            lblTime.setText(time);
        } else if (actionCommand.equals("reminderTimer") && todayReminderList != null) {
            Iterator it = todayReminderList.iterator();
            while (it.hasNext()) {
                Reminder reminder = (Reminder) it.next();
                if(isAlert(reminder)){
                    this.setVisible(true);
                    ReminderDialog reminderDialog = new ReminderDialog(this, true, reminder);
                    reminderDialog.setLocationRelativeTo(null);
                    reminderDialog.setVisible(true);
                    this.setVisible(false);
                }
            }
        } else if (actionCommand.equals("trayExit")) {
            logger.info("Exiting Application...");
            systemTray.remove(trayIcon);
            System.exit(0);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="MouseListener">
    public void mouseClicked(MouseEvent e) {
        String source = e.getSource().getClass().getSimpleName();
       
        if (e.getButton() == MouseEvent.BUTTON1 && source.equals("TrayIcon")) {
            this.setVisible(true);
            this.setExtendedState(JFrame.MAXIMIZED_BOTH);
        }
    }

    public void mousePressed(MouseEvent e) {}

    public void mouseReleased(MouseEvent e) {}

    public void mouseEntered(MouseEvent e) {}

    public void mouseExited(MouseEvent e) {}
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="KeyListener">
    @Override
    public void keyTyped(KeyEvent e) {
//        if (e.isAltDown() && (e.getKeyChar() == 'c' || e.getKeyChar() == 'C')) {
//            DateDialog dd = new DateDialog(this, true);
//            dd.addPropertyChangeListener(this);
//            dd.setLocationRelativeTo(null);
//            dd.setVisible(true);
//        }
    }

    @Override
    public void keyPressed(KeyEvent e) {}

    @Override
    public void keyReleased(KeyEvent e) {}
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="WindowListener">
    public void windowOpened(WindowEvent e) {}

    public void windowClosing(WindowEvent e) {
        this.setVisible(false);
    }

    public void windowClosed(WindowEvent e) {}

    public void windowIconified(WindowEvent e) {
        this.setVisible(false);
    }
    
    public void windowDeiconified(WindowEvent e) {}

    public void windowActivated(WindowEvent e) {}
    
    public void windowDeactivated(WindowEvent e) {}
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Variable Declaration">    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel hourComboPanel;
    private javax.swing.JLabel lblDate;
    private javax.swing.JLabel lblHour;
    private javax.swing.JLabel lblTime;
    private javax.swing.JLabel lblYear;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JMenuItem mnuItemGotoDate;
    private javax.swing.JMenuItem mnuItemInputText;
    private javax.swing.JMenu mnuTools;
    private javax.swing.JPanel pnCalendar;
    private javax.swing.JPanel pnCalendarWest;
    private javax.swing.JPanel pnDateTimeNorth;
    private javax.swing.JPanel pnReminder;
    private javax.swing.JPanel pnReminderCenter;
    private javax.swing.JPanel pnTaskCenter;
    private javax.swing.JPanel pnYear;
    private javax.swing.JScrollPane spCalendar;
    private javax.swing.JScrollPane spReminderCenter;
    private javax.swing.JTabbedPane tpTaskCenter;
    // End of variables declaration//GEN-END:variables
//</editor-fold>

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
                                    @Override
                                    public void run() {
                                        MainFrame frame = new MainFrame();
                                        //frame.setPreferredSize(new Dimension(1024, 800));
                                        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
                                        //frame.setLocationRelativeTo(null);  // center the window
                                        frame.setVisible(true);
                                    }
                                });
    }
}